看完記憶體儲存差異,現在要來談談全域污染這件事。
所謂的範疇Scope是規範變數有效的區域
在JavaScript內有三個主要的變數有效範疇:
透過下面這段程式碼來標示所謂的scope:
let x = 2;
let y = 2;
function trys(){
    let x = 3;
    let y = 3;
}
{
    let x = 4;
    let y = 4;
}

不同的宣告有不同的變數有效範疇,以下簡單以表格呈現:
| 宣告方式 | var | let | const | 
|---|---|---|---|
| 有效區域 | global scope / function scope | global scope / funciotn scope / block scope | global scope / funciotn scope / block scope | 
有效區域指的是什麼意思,也直接看例子吧?
//區域範疇內有x變數
{
    let x = 3;
    
}
console.log(x);

可以理解成x這個宣告,出了這個區域就會無效(無法取得)。
其實之所以會出現所謂scope的現象,背後是由字彙環境一手包辦的,什麼意思?
字彙環境(lexical environment)的任務就是:追蹤紀錄範疇內(scope)的識別字
let x = 2;
let y = 2;
function trys(){
    let x = 3;
    let y = 3;
}
{
    let x = 4;
    let y = 4;
}
會透過字彙環境紀錄自己區域內的宣告內容,在你需要使用時就會來找尋
當你要找尋的變數,不存在當前的字彙環境中,它會往上層的字彙環境找尋,若到了全域都沒有此變數,則會回報reference error。
假設我們今天在函式內會取用到z這個變數,但它不存在:
let z = "我在全域";
function doIt(i){
    console.log(z);
}
doIt();

最後由圖可知,變數z會在全域找到。
理念大概理解,用實例來試試看吧
for迴圈結構!來看一個經典的範疇(scope)相關題目
var宣告時for(var i =0;i <4;i++){
    setTimeout(function(){
        console.log(i,"我是結果");
    },1000)
}
你想像的執行過程跟的結果
i = 0; console.log(0,"我是結果");
i = 1; console.log(1,"我是結果");
i = 2; console.log(2,"我是結果");   
i = 3; console.log(3,"我是結果");
i = 4; console.log(4,"我是結果");
//0,"我是結果";
//1,"我是結果";
//2,"我是結果";
//3,"我是結果";
//4,"我是結果";
實際上的結果
因為
var跟let的差別前面有提過兩者的範疇上的差異:
用var時,因為它不具有block scope,所以並不存在{}的字彙環境,因此不會紀錄i的值,所以會往global environment尋找此值,
i = 0;
i = 1;
i = 2;
i = 3;
i = 4;
console.log(4);
console.log(4);
console.log(4);
console.log(4);
console.log(4);
而因為非同步的執行,所以當要印出所有i時,global environment所紀錄的值,已經是迭代完成的值,也就是4
。
那為何let就可以?
前面提過let具有block environment的特性,所以每次迭代時,都會在{}建立字彙環境,並記住當前i的值。
所以你可以想像為:
//開始迭代: for environment
{    
    let i =0;
    console.log(i);
}
//第一圈後: for environment
{
    let i =1;
    console.log(i);
}
...
{
    let i = 4;
    console.log(i);
}
不過上述的論述,只是在學習過程與前輩跟同學討論的整理,並沒有在規範中明確定義。
-- to be continued --
那今天就到這邊摟!
每天的休息,是為了後面的追求,明天見。